<?php

namespace Henan\ThinkSdk\traits;


use Exception;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Henan\ThinkSdk\helper\FC;
use think\Request;

/**
 * 登录特征
 * @author henan
 */
trait LoginTrait
{
    use ResponseTrait;
    use ValidateTrait;

    /**
     * 登录
     * @param string $model 模型
     * @param string $username 账号字段
     * @param string $password 密码字段
     * @param string $status 状态字段
     * @param string $uid uid字段
     * @param int $expire 过期时间
     * @param string $key 加密值
     * @param callable|null $afterFun 回调函数
     * @return void 响应输出
     */
    protected function onLogin(
        string   $model,
        string   $username = 'username',
        string   $password = 'password',
        string   $status = 'status',
        string   $uid = 'id',
        int      $expire = 60 * 60 * 24 * 7,
        string   $key = 'token',
        bool     $is_md5 = false,
        callable $afterFun = null
    ): void
    {
        $param = $this->check([$username, $password], 'post');
        try {
            $user = (new $model)->where($username, $param[$username])->find();
            if (empty($user)) throw new Exception('账号不存在');
            if ($is_md5) $param[$password] = md5($param[$password]);
            if ($user[$password] != $param[$password]) throw new Exception('密码输入有误');
            if (!$user[$status]) throw new Exception('账号已被禁用');
            $iat = time();
            $exp = time() + $expire;
            $payload = [
                "iat" => $iat, // 签发时间(生效时间)
                "exp" => $exp, // 过期时间
                "data" => ['uid' => $user[$uid]], // 拓展信息
            ];
            $token = JWT::encode($payload, $key, 'HS256');
            $userinfo = $user->toArray();
            unset($userinfo[$password]);
            $data = ['token' => $token, 'iat' => $iat, 'exp' => $exp, 'userinfo' => $userinfo];
            if ($afterFun) $data = $afterFun($data);
        } catch (Exception $e) {
            $this->error($e->getMessage());
        }
        $this->success($data, '登录成功');
    }

    /**
     * 检查登录
     * @param string $model 模型
     * @param Request $request 请求对象
     * @param array $noLoginUrls 免登录地址
     * @param string $authName 凭证请求字段名称
     * @param string $status 状态字段
     * @param string $key 加密值
     * @return void
     */
    protected function onCheckLogin(
        string  $model,
        Request $request,
        array   $noLoginUrls = [],
        string  $authName = 'token',
        string  $status = 'status',
        string  $key = 'token',
    ): void
    {
        $currentUri = FC::getUrl();
        foreach ($noLoginUrls as $pattern) {
            $pattern = str_replace('*', '.*', $pattern);
            if (preg_match('#^' . $pattern . '$#', $currentUri)) {
                return;
            }
        }
        $token = $request->header($authName);
        if (empty($token)) $this->error('缺少登录凭证', self::$INVALID_TOKEN_CODE);
        try {
            $jwt = JWT::decode($token, new Key($key, 'HS256'));
        } catch (Exception) {
            $this->error('登录凭证无效，请重新登录', self::$INVALID_TOKEN_CODE);
        }
        try {
            $iat = $jwt->iat;
            $exp = $jwt->exp;
            $data = $jwt->data;
            $uid = $jwt->data?->uid;
            if (empty($uid)) throw new Exception('登录凭证无效，请重新登录', self::$INVALID_TOKEN_CODE);
            $user = (new $model)->find($uid);
            if (empty($user)) throw new Exception('账号不存在', self::$INVALID_ACCOUNT_CODE);
            if (!$user[$status]) throw new Exception('账号已被禁用', self::$DISABLE_ACCOUNT_CODE);
            $request->withMiddleware(['uid' => $uid, 'user' => $user, 'token' => $token, 'iat' => $iat, 'exp' => $exp, 'data' => $data]);
        } catch (Exception $e) {
            $this->error($e->getMessage(), $e->getCode());
        }
    }
}